﻿#region using...
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using NsMenu = Infusion.Silverlight.Controls.Menu;
#endregion

namespace Infusion.Silverlight.Controls.Ribbon
{
  #region : Ribbon :
  #region Template Parts...
  [TemplatePart(Name = Ribbon.RibbonLayoutRoot, Type = typeof(Canvas))]
  [TemplatePart(Name = Ribbon.RibbonLayoutRootClip, Type = typeof(RectangleGeometry))]
  [TemplatePart(Name = Ribbon.RibbonBody, Type = typeof(Canvas))]
  [TemplatePart(Name = Ribbon.RibbonBodyClip, Type = typeof(RectangleGeometry))]
  [TemplatePart(Name = Ribbon.RibbonBodyRectOutline, Type = typeof(Rectangle))]
  [TemplatePart(Name = Ribbon.RibbonTabs, Type = typeof(Grid))]
  [TemplatePart(Name = Ribbon.ToolTip, Type = typeof(RibbonToolTip))]
  [TemplatePart(Name = Ribbon.TitleBar, Type = typeof(Canvas))]
  [TemplatePart(Name = Ribbon.OfficeButtonLayout, Type = typeof(Grid))]
  [TemplatePart(Name = Ribbon.AnimateHighlight, Type = typeof(Storyboard))]
  [TemplatePart(Name = Ribbon.ColorAnimationEllipseOfficeButtonBorder, Type = typeof(ColorAnimation))]
  [TemplatePart(Name = Ribbon.ColorAnimationEllipseOfficeButton, Type = typeof(ColorAnimation))]
  [TemplatePart(Name = Ribbon.ColorAnimationEllipseOfficeButtonGradient0, Type = typeof(ColorAnimation))]
  [TemplatePart(Name = Ribbon.ColorAnimationEllipseOfficeButtonGradient1, Type = typeof(ColorAnimation))]
  [TemplatePart(Name = Ribbon.ColorAnimationPathOfficeButtonHighlightGradient0, Type = typeof(ColorAnimation))]
  [TemplatePart(Name = Ribbon.ColorAnimationPathOfficeButtonHighlightGradient1, Type = typeof(ColorAnimation))]
  [TemplatePart(Name = Ribbon.EllipseOfficeButtonBorder, Type = typeof(Ellipse))]
  [TemplatePart(Name = Ribbon.EllipseOfficeButton, Type = typeof(Ellipse))]
  [TemplatePart(Name = Ribbon.PathOfficeButtonHighlight, Type = typeof(Path))]
  [TemplatePart(Name = Ribbon.GridQuickLaunchButtons, Type = typeof(Grid))]
  [TemplatePart(Name = Ribbon.CanvasQuickLaunchRightCap, Type = typeof(Canvas))]
  [TemplatePart(Name = Ribbon.CanvasOfficeButtonClickSurface, Type = typeof(Canvas))]
  [TemplatePart(Name = Ribbon.ImgOfficeButton, Type = typeof(Image))]
  [TemplatePart(Name = Ribbon.MainMenu, Type = typeof(RibbonMainMenu))]
  [TemplatePart(Name = Ribbon.StoryboardMenuOpacity, Type = typeof(Storyboard))]
  #endregion
  [ContentProperty("Tabs")]
  public class Ribbon : Control
  {
    #region TemplatePart Names...
    public const string RibbonLayoutRoot = "RibbonLayoutRoot";
    public const string RibbonLayoutRootClip = "RibbonLayoutRootClip";
    public const string RibbonBody = "RibbonBody";
    public const string RibbonBodyClip = "RibbonBodyClip";
    public const string RibbonBodyRectOutline = "RibbonBodyRectOutline";
    public const string RibbonTabs = "RibbonTabs";
    public const string ToolTip = "ToolTip";
    public const string TitleBar = "TitleBar";
    public const string OfficeButtonLayout = "OfficeButtonLayout";
    public const string AnimateHighlight = "AnimateHighlight";
    public const string ColorAnimationEllipseOfficeButtonBorder = "ColorAnimationEllipseOfficeButtonBorder";
    public const string ColorAnimationEllipseOfficeButton = "ColorAnimationEllipseOfficeButton";
    public const string ColorAnimationEllipseOfficeButtonGradient0 = "ColorAnimationEllipseOfficeButtonGradient0";
    public const string ColorAnimationEllipseOfficeButtonGradient1 = "ColorAnimationEllipseOfficeButtonGradient1";
    public const string ColorAnimationPathOfficeButtonHighlightGradient0 = "ColorAnimationPathOfficeButtonHighlightGradient0";
    public const string ColorAnimationPathOfficeButtonHighlightGradient1 = "ColorAnimationPathOfficeButtonHighlightGradient1";
    public const string EllipseOfficeButtonBorder = "EllipseOfficeButtonBorder";
    public const string EllipseOfficeButton = "EllipseOfficeButton";
    public const string PathOfficeButtonHighlight = "PathOfficeButtonHighlight";
    public const string GridQuickLaunchButtons = "GridQuickLaunchButtons";
    public const string CanvasQuickLaunchRightCap = "CanvasQuickLaunchRightCap";
    public const string CanvasOfficeButtonClickSurface = "CanvasOfficeButtonClickSurface";
    public const string ImgOfficeButton = "ImgOfficeButton";
    public const string MainMenu = "MainMenu";
    public const string StoryboardMenuOpacity = "StoryboardMenuOpacity";
    #endregion

    #region TemplatePart Variables...
    internal Canvas ribbonLayoutRoot;
    internal RectangleGeometry ribbonLayoutRootClip;
    internal Canvas ribbonBody;
    internal RectangleGeometry ribbonBodyClip;
    internal Rectangle ribbonBodyRectOutline;
    internal Grid ribbonTabs;
    internal RibbonToolTip toolTip;
    internal Canvas titleBar;
    internal Grid officeButtonLayout;
    internal Storyboard animateHighlight;
    internal ColorAnimation colorAnimationEllipseOfficeButtonBorder;
    internal ColorAnimation colorAnimationEllipseOfficeButton;
    internal ColorAnimation colorAnimationEllipseOfficeButtonGradient0;
    internal ColorAnimation colorAnimationEllipseOfficeButtonGradient1;
    internal ColorAnimation colorAnimationPathOfficeButtonHighlightGradient0;
    internal ColorAnimation colorAnimationPathOfficeButtonHighlightGradient1;
    internal Ellipse ellipseOfficeButtonBorder;
    internal Ellipse ellipseOfficeButton;
    internal Path pathOfficeButtonHighlight;
    internal Grid gridQuickLaunchButtons;
    internal Canvas canvasQuickLaunchRightCap;
    internal Canvas canvasOfficeButtonClickSurface;
    internal Image imgOfficeButton;
    internal RibbonMainMenu mainMenu;
    internal Storyboard storyboardMenuOpacity;
    #endregion

    #region Class Variables...
    private bool hasLoaded = false;
    private bool templateApplied = false;
    private bool officeButtonClicked = false;
    private NsMenu.Menu openMenu = null;
    private Dictionary<RibbonTab, Grid> tabContentDictionary;
    #endregion

    #region Dependency Properties...
    #region TabsProperty
    /// <summary>
    /// Identifies the Infusion.Silverlight.Controls.Ribbon.Ribbon.Tabs dependency property.
    /// </summary>
    public static readonly DependencyProperty TabsProperty = DependencyProperty.Register(
        "Tabs",
        typeof(RibbonTabCollection),
        typeof(Ribbon),
        new PropertyMetadata(null, OnTabsPropertyChanged));

    private static void OnTabsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
      RibbonTabCollection rtc = e.NewValue as RibbonTabCollection;
      if (rtc != null)
      {
        foreach (RibbonTab tab in rtc)
        {
          tab.ParentRibbon = (Ribbon)d;

          Grid groupGrid = new Grid();
          groupGrid.SetValue(Canvas.TopProperty, 25d); 
          groupGrid.SetValue(Canvas.LeftProperty, 4d);
          groupGrid.ColumnDefinitions.Add(Helper.CreateGridColumnDefinition(0, GridUnitType.Auto));
          groupGrid.RowDefinitions.Add(Helper.CreateGridRowDefinition(85d));

          (((Ribbon)d).tabContentDictionary).Add(tab, groupGrid);
        }
      }
    }
    #endregion

    #region ToolTipsProperty
    /// <summary>
    /// Identifies the Infusion.Silverlight.Controls.Ribbon.Ribbon.ToolTips dependency property.
    /// </summary>
    public static readonly DependencyProperty ToolTipsProperty = DependencyProperty.Register(
        "ToolTips",
        typeof(ToolTipCollection),
        typeof(Ribbon),
        null);
    #endregion

    #region MenusProperty
    /// <summary>
    /// Identifies the Infusion.Silverlight.Controls.Ribbon.Ribbon.Menus dependency property.
    /// </summary>
    public static readonly DependencyProperty MenusProperty = DependencyProperty.Register(
        "Menus",
        typeof(MenuCollection),
        typeof(Ribbon),
        null);
    #endregion

    #region SelectedTabIndexProperty
    /// <summary>
    /// Identifies the Infusion.Silverlight.Controls.Ribbon.Ribbon.SelectedTabIndex dependency property.
    /// </summary>
    public static readonly DependencyProperty SelectedTabIndexProperty = DependencyProperty.Register(
        "SelectedTabIndex",
        typeof(int),
        typeof(Ribbon),
        new PropertyMetadata(new PropertyChangedCallback(SelectedTabIndexPropertyChanged)));
    #endregion

    #region OfficeButtonImageSourceProperty
    /// <summary>
    /// Identifies the Infusion.Silverlight.Controls.Ribbon.Ribbon.OfficeButtonImageSource dependency property.
    /// </summary>
    public static readonly DependencyProperty OfficeButtonImageSourceProperty = DependencyProperty.Register(
        "OfficeButtonImageSource",
        typeof(string),
        typeof(Ribbon),
        null);
    #endregion

    #region QuickLaunchButtonsProperty
    /// <summary>
    /// Identifies the Infusion.Silverlight.Controls.Ribbon.Ribbon.QuickLaunchButtons dependency property.
    /// </summary>
    public static readonly DependencyProperty QuickLaunchButtonsProperty = DependencyProperty.Register(
        "QuickLaunchButtons",
        typeof(QuickLaunchButtonCollection),
        typeof(Ribbon),
        null);
    #endregion
    #endregion

    #region Dependency Property Changed Handlers...
    #region SelectedTabIndexPropertyChanged
    private static void SelectedTabIndexPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
    {
      if (args.NewValue != null)
      {
        Ribbon ribbon = (Ribbon)obj;

        if (ribbon.hasLoaded)
        {
          int oldIndex = (int)args.OldValue;
          int newIndex = (int)args.NewValue;

          if (newIndex >= 0 && newIndex < ribbon.Tabs.Count)
          {
            if (oldIndex >= 0 && oldIndex < ribbon.Tabs.Count)
            {
              RibbonTab oldSelected = (RibbonTab)ribbon.Tabs[oldIndex];
              oldSelected.Selected = false;
            }

            RibbonTab newSelected = (RibbonTab)ribbon.Tabs[newIndex];
            newSelected.Selected = true;
          }
        }
      }
    }
    #endregion
    #endregion

    #region Constructor(s)...
    #region Ribbon
    public Ribbon()
      : base()
    {
      DefaultStyleKey = typeof(Ribbon);
      IsTabStop = true;

      Tabs = new RibbonTabCollection();
      Tabs.CollectionChanged += new NotifyCollectionChangedEventHandler(Tabs_CollectionChanged);

      Menus = new MenuCollection();
      Menus.CollectionChanged += new NotifyCollectionChangedEventHandler(Menus_CollectionChanged);

      QuickLaunchButtons = new QuickLaunchButtonCollection();
      ToolTips = new ToolTipCollection();

      Loaded += new RoutedEventHandler(Ribbon_Loaded);

      tabContentDictionary = new Dictionary<RibbonTab, Grid>();
      SelectedTabIndex = -1;
    }
    #endregion
    #endregion

    #region Event Handlers...
    #region ribbonLayoutRoot_SizeChanged
    private void ribbonLayoutRoot_SizeChanged(object sender, SizeChangedEventArgs e)
    {
      if (e.NewSize.Width > 1)
      {
        ribbonBody.Width = e.NewSize.Width - 2d;
        ribbonBodyClip.Rect = new Rect(0d, 0d, e.NewSize.Width > 1 ? e.NewSize.Width - 2d : 0d, ribbonBody.Height);

        ribbonBodyRectOutline.Width = e.NewSize.Width - 1d;
        ribbonBodyRectOutline.Height = ribbonBody.Height + 1d;
      }
    }
    #endregion

    #region Tabs_CollectionChanged
    private void Tabs_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
      switch (e.Action)
      {
        case NotifyCollectionChangedAction.Add:
          {
            foreach (RibbonTab tab in e.NewItems)
            {
              tab.ParentRibbon = this;

              Grid groupGrid = new Grid();
              groupGrid.SetValue(Canvas.TopProperty, 27d); //26d);
              groupGrid.SetValue(Canvas.LeftProperty, 4d);
              groupGrid.ColumnDefinitions.Add(Helper.CreateGridColumnDefinition(0, GridUnitType.Auto));
              groupGrid.RowDefinitions.Add(Helper.CreateGridRowDefinition(85d));

              tabContentDictionary.Add(tab, groupGrid);
            }
            break;
          }
        case NotifyCollectionChangedAction.Remove:
          {
            foreach (RibbonTab tab in e.NewItems)
            {
              tabContentDictionary.Remove(tab);
            }
            break;
          }
        case NotifyCollectionChangedAction.Replace:
          break;
        case NotifyCollectionChangedAction.Reset:
          break;
        default:
          break;
      }
    }
    #endregion

    #region Menus_CollectionChanged
    private void Menus_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
      switch (e.Action)
      {
        case NotifyCollectionChangedAction.Add:
          foreach (NsMenu.Menu menu in Menus)
          {
            if (ribbonLayoutRoot != null)
              ribbonLayoutRoot.Children.Add(menu);
          }
          break;
        case NotifyCollectionChangedAction.Remove:
          foreach (NsMenu.Menu menu in Menus)
          {
            if (ribbonLayoutRoot != null)
              ribbonLayoutRoot.Children.Remove(menu);
          }
          break;
        case NotifyCollectionChangedAction.Replace:
          break;
        case NotifyCollectionChangedAction.Reset:
          break;
        default:
          break;
      }
    }
    #endregion

    #region Ribbon_Loaded
    private void Ribbon_Loaded(object sender, RoutedEventArgs e)
    {
      hasLoaded = true;
      ((FrameworkElement)Application.Current.RootVisual).MouseLeftButtonDown += new MouseButtonEventHandler(Ribbon_MouseLeftButtonDown);
    }
    #endregion

    #region Ribbon_MouseLeftButtonDown
    private void Ribbon_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
      if (HasMenuOpen)
      {
        //List<UIElement> hits = (List<UIElement>)HitTest(e.GetPosition(null));
        List<UIElement> hits = (List<UIElement>)VisualTreeHelper.FindElementsInHostCoordinates(e.GetPosition(null), this);
        if (!hits.Contains(openMenu))
        {
          HideOpenMenu();
        }
      }
    }
    #endregion

    #region RibbonTab_Loaded
    private void RibbonTab_Loaded(object sender, RoutedEventArgs e)
    {
      RibbonTab tab = (RibbonTab)sender;
      tab.HasLoaded = true;
      tabContentDictionary[tab].Visibility = Visibility.Collapsed;

      if (Tabs.AllLoaded)
      {
        if (SelectedTabIndex < 0 || SelectedTabIndex > Tabs.Count - 1)
        {
          SelectedTabIndex = 0;
        }
        else
        {
          Tabs[SelectedTabIndex].Selected = true;
        }
      }
    }
    #endregion

    #region canvasOfficeButtonClickSurface_MouseEnter
    private void canvasOfficeButtonClickSurface_MouseEnter(object sender, MouseEventArgs e)
    {
      if (!officeButtonClicked)
      {
        ellipseOfficeButtonBorder.Stroke = Helper.GetBrush(SolidColorBrushes.OfficeButtonBorderHover);
        ellipseOfficeButton.Fill = Helper.GetBrush(LinearGradientBrushes.OfficeButtonBackgroundHover);
        ellipseOfficeButton.Stroke = Helper.GetBrush(SolidColorBrushes.OfficeButtonBackgroundBorderHover);
        pathOfficeButtonHighlight.Fill = Helper.GetBrush(RadialGradientBrushes.OfficeButtonHighlightHover);
      }
    }
    #endregion

    #region canvasOfficeButtonClickSurface_MouseLeave
    private void canvasOfficeButtonClickSurface_MouseLeave(object sender, MouseEventArgs e)
    {
      if (!officeButtonClicked)
      {
        SetupOfficeButtonColorAnimation(false);
        animateHighlight.Stop();
        animateHighlight.Begin();
      }
    }
    #endregion

    #region canvasOfficeButtonClickSurface_MouseLeftButtonDown
    private void canvasOfficeButtonClickSurface_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
      ToggleOfficeButton();
    }
    #endregion

    #region gridQuickLaunchButtons_LayoutUpdated
    private void gridQuickLaunchButtons_LayoutUpdated(object sender, EventArgs e)
    {
      double columnWidthSum = 0d;

      foreach (ColumnDefinition colDef in gridQuickLaunchButtons.ColumnDefinitions)
      {
        columnWidthSum += colDef.ActualWidth;
      }

      canvasQuickLaunchRightCap.SetValue(Canvas.LeftProperty, 45d + columnWidthSum);
    }
    #endregion

    #region storyboardMenuOpacity_Completed
    private void storyboardMenuOpacity_Completed(object sender, EventArgs e)
    {
      mainMenu.Visibility = Visibility.Collapsed;
    }
    #endregion
    #endregion

    #region Methods...
    #region ToggleOfficeButton
    internal void ToggleOfficeButton()
    {
      officeButtonClicked = !officeButtonClicked;

      if (officeButtonClicked)
      {
        ellipseOfficeButtonBorder.Stroke = Helper.GetBrush(SolidColorBrushes.OfficeButtonBorderClick);
        ellipseOfficeButton.Fill = Helper.GetBrush(LinearGradientBrushes.OfficeButtonBackgroundClick);
        ellipseOfficeButton.Stroke = Helper.GetBrush(SolidColorBrushes.OfficeButtonBackgroundBorderClick);
        pathOfficeButtonHighlight.Fill = Helper.GetBrush(RadialGradientBrushes.OfficeButtonHighlightClick);

        mainMenu.Opacity = 1;
        mainMenu.Visibility = Visibility.Visible;
      }
      else
      {
        SetupOfficeButtonColorAnimation(true);
        animateHighlight.Stop();
        animateHighlight.Begin();

        storyboardMenuOpacity.Begin();
      }
    }
    #endregion

    #region SetupOfficeButtonColorAnimation
    private void SetupOfficeButtonColorAnimation(bool targetStateIsHover)
    {
      SolidColorBrush borderBrush, backgroundBorderBrush;
      LinearGradientBrush backgroundBrush;
      RadialGradientBrush highlightBrush;

      if (!targetStateIsHover)
      {
        borderBrush = Helper.GetBrush(SolidColorBrushes.OfficeButtonBorder) as SolidColorBrush;
        backgroundBorderBrush = Helper.GetBrush(SolidColorBrushes.OfficeButtonBackgroundBorder) as SolidColorBrush;
        backgroundBrush = Helper.GetBrush(LinearGradientBrushes.OfficeButtonBackground) as LinearGradientBrush;
        highlightBrush = Helper.GetBrush(RadialGradientBrushes.OfficeButtonHighlight) as RadialGradientBrush;
      }
      else
      {
        borderBrush = Helper.GetBrush(SolidColorBrushes.OfficeButtonBorderHover) as SolidColorBrush;
        backgroundBorderBrush = Helper.GetBrush(SolidColorBrushes.OfficeButtonBackgroundBorderHover) as SolidColorBrush;
        backgroundBrush = Helper.GetBrush(LinearGradientBrushes.OfficeButtonBackgroundHover) as LinearGradientBrush;
        highlightBrush = Helper.GetBrush(RadialGradientBrushes.OfficeButtonHighlightHover) as RadialGradientBrush;
      }

      colorAnimationEllipseOfficeButtonBorder.From = ((SolidColorBrush)ellipseOfficeButtonBorder.Stroke).Color;
      colorAnimationEllipseOfficeButtonBorder.To = borderBrush.Color;

      colorAnimationEllipseOfficeButton.From = ((SolidColorBrush)ellipseOfficeButton.Stroke).Color;
      colorAnimationEllipseOfficeButton.To = backgroundBorderBrush.Color;

      colorAnimationEllipseOfficeButtonGradient0.From = ((LinearGradientBrush)ellipseOfficeButton.Fill).GradientStops[0].Color;
      colorAnimationEllipseOfficeButtonGradient0.To = backgroundBrush.GradientStops[0].Color;
      colorAnimationEllipseOfficeButtonGradient1.From = ((LinearGradientBrush)ellipseOfficeButton.Fill).GradientStops[1].Color;
      colorAnimationEllipseOfficeButtonGradient1.To = backgroundBrush.GradientStops[1].Color;

      colorAnimationPathOfficeButtonHighlightGradient0.From = ((RadialGradientBrush)pathOfficeButtonHighlight.Fill).GradientStops[0].Color;
      colorAnimationPathOfficeButtonHighlightGradient0.To = highlightBrush.GradientStops[0].Color;
      colorAnimationPathOfficeButtonHighlightGradient1.From = ((RadialGradientBrush)pathOfficeButtonHighlight.Fill).GradientStops[1].Color;
      colorAnimationPathOfficeButtonHighlightGradient1.To = highlightBrush.GradientStops[1].Color;
    }
    #endregion

    #region OnApplyTemplate
    public override void OnApplyTemplate()
    {
      base.OnApplyTemplate();

      if (!templateApplied)
      {
        ribbonLayoutRoot = (Canvas)GetTemplateChild(RibbonLayoutRoot);
        ribbonLayoutRootClip = (RectangleGeometry)GetTemplateChild(RibbonLayoutRootClip);
        ribbonBody = (Canvas)GetTemplateChild(RibbonBody);
        ribbonBodyClip = (RectangleGeometry)GetTemplateChild(RibbonBodyClip);
        ribbonBodyRectOutline = (Rectangle)GetTemplateChild(RibbonBodyRectOutline);
        ribbonTabs = (Grid)GetTemplateChild(RibbonTabs);
        toolTip = (RibbonToolTip)GetTemplateChild(ToolTip);

        titleBar = (Canvas)GetTemplateChild(TitleBar);
        officeButtonLayout = (Grid)GetTemplateChild(OfficeButtonLayout);
        animateHighlight = (Storyboard)GetTemplateChild(AnimateHighlight);
        colorAnimationEllipseOfficeButtonBorder = (ColorAnimation)GetTemplateChild(ColorAnimationEllipseOfficeButtonBorder);
        colorAnimationEllipseOfficeButton = (ColorAnimation)GetTemplateChild(ColorAnimationEllipseOfficeButton);
        colorAnimationEllipseOfficeButtonGradient0 = (ColorAnimation)GetTemplateChild(ColorAnimationEllipseOfficeButtonGradient0);
        colorAnimationEllipseOfficeButtonGradient1 = (ColorAnimation)GetTemplateChild(ColorAnimationEllipseOfficeButtonGradient1);
        colorAnimationPathOfficeButtonHighlightGradient0 = (ColorAnimation)GetTemplateChild(ColorAnimationPathOfficeButtonHighlightGradient0);
        colorAnimationPathOfficeButtonHighlightGradient1 = (ColorAnimation)GetTemplateChild(ColorAnimationPathOfficeButtonHighlightGradient1);
        ellipseOfficeButtonBorder = (Ellipse)GetTemplateChild(EllipseOfficeButtonBorder);
        ellipseOfficeButton = (Ellipse)GetTemplateChild(EllipseOfficeButton);
        pathOfficeButtonHighlight = (Path)GetTemplateChild(PathOfficeButtonHighlight);
        gridQuickLaunchButtons = (Grid)GetTemplateChild(GridQuickLaunchButtons);
        canvasQuickLaunchRightCap = (Canvas)GetTemplateChild(CanvasQuickLaunchRightCap);
        canvasOfficeButtonClickSurface = (Canvas)GetTemplateChild(CanvasOfficeButtonClickSurface);
        imgOfficeButton = (Image)GetTemplateChild(ImgOfficeButton);
        mainMenu = (RibbonMainMenu)GetTemplateChild(MainMenu);
        storyboardMenuOpacity = (Storyboard)GetTemplateChild(StoryboardMenuOpacity);

        mainMenu.Visibility = Visibility.Collapsed;
        mainMenu.SetValue(Canvas.ZIndexProperty, 99998);
        officeButtonLayout.SetValue(Canvas.ZIndexProperty, 99999);

        storyboardMenuOpacity.Completed += new EventHandler(storyboardMenuOpacity_Completed);

        ribbonLayoutRoot.SizeChanged += new SizeChangedEventHandler(ribbonLayoutRoot_SizeChanged);
        canvasOfficeButtonClickSurface.MouseEnter += new MouseEventHandler(canvasOfficeButtonClickSurface_MouseEnter);
        canvasOfficeButtonClickSurface.MouseLeave += new MouseEventHandler(canvasOfficeButtonClickSurface_MouseLeave);
        canvasOfficeButtonClickSurface.MouseLeftButtonDown += new MouseButtonEventHandler(canvasOfficeButtonClickSurface_MouseLeftButtonDown);
        gridQuickLaunchButtons.LayoutUpdated += new EventHandler(gridQuickLaunchButtons_LayoutUpdated);

        if (!string.IsNullOrEmpty(OfficeButtonImageSource))
          imgOfficeButton.Source = new BitmapImage(new Uri(OfficeButtonImageSource, UriKind.Relative));

        //tabs...

        if (tabContentDictionary.Count == Tabs.Count)
        {
          for (int tabIndex = 0; tabIndex < Tabs.Count; tabIndex++)
          {
            if (ribbonTabs.ColumnDefinitions.Count - 1 < tabIndex + 2)
              ribbonTabs.ColumnDefinitions.Insert(tabIndex + 1, Helper.CreateGridColumnDefinition(0, GridUnitType.Auto));

            Tabs[tabIndex].SetValue(Grid.ColumnProperty, tabIndex + 1);
            Tabs[tabIndex].SetValue(Grid.RowProperty, 0);
            Tabs[tabIndex].Loaded += new RoutedEventHandler(RibbonTab_Loaded);

            ribbonTabs.Children.Insert(tabIndex + 1, Tabs[tabIndex]);
            ribbonLayoutRoot.Children.Add(tabContentDictionary[Tabs[tabIndex]]);
          }
        }

        ((RibbonButton)ribbonTabs.Children[0]).SetValue(Grid.ColumnProperty, Tabs.Count + 1);
        ((RibbonButton)ribbonTabs.Children[0]).ParentRibbon = this;

        //menus...
        foreach (NsMenu.Menu menu in Menus)
        {
          ribbonLayoutRoot.Children.Add(menu);
        }

        //quick launch buttons...
        for (int quickButtonIndex = 0; quickButtonIndex < QuickLaunchButtons.Count; quickButtonIndex++)
        {
          if (gridQuickLaunchButtons.ColumnDefinitions.Count - 1 < quickButtonIndex)
            gridQuickLaunchButtons.ColumnDefinitions.Add(Helper.CreateGridColumnDefinition(0, GridUnitType.Auto));

          QuickLaunchButtons[quickButtonIndex].ParentRibbon = this;
          QuickLaunchButtons[quickButtonIndex].Format = ButtonFormat.Small;
          QuickLaunchButtons[quickButtonIndex].SetValue(Grid.ColumnProperty, quickButtonIndex);

          gridQuickLaunchButtons.Children.Add(QuickLaunchButtons[quickButtonIndex]);
        }

        templateApplied = true;
      }
    }
    #endregion

    #region HideOpenMenu
    internal void HideOpenMenu()
    {
      if (openMenu != null)
      {
        openMenu.Hide(true);
        openMenu = null;
      }
    }
    #endregion

    #region ShowMenu
    public NsMenu.Menu ShowMenu(string menuID, Point menuLocation, bool useAnimation)
    {
      foreach (NsMenu.Menu menu in Menus)
      {
        if (menuID == menu.ID)
        {
          openMenu = menu;
          break;
        }
      }

      if (openMenu == null)
      {
        return null;
        //throw new Exception(string.Format("MenuID '{0}' not found in the MenuCollection", menuID));
      }

      openMenu.Closing += new EventHandler(openMenu_Closing);

      double difference = ((FrameworkElement)Application.Current.RootVisual).ActualWidth - menuLocation.X - openMenu.RealWidth;
      openMenu.Show(useAnimation, new Point(menuLocation.X + (difference < 0 ? difference : 0), menuLocation.Y));
      return openMenu;
    }

    private void openMenu_Closing(object sender, EventArgs e)
    {
      if (openMenu != null)
        openMenu.Closing -= new EventHandler(openMenu_Closing);
      openMenu = null;
    }
    #endregion

    #region ShowToolTip
    internal void ShowToolTip(string toolTipID, Point toolTipLocation, bool isAlternative)
    {
      bool foundInCollection = false;

      foreach (ToolTip toolTipItem in ToolTips)
      {
        if (toolTipID == toolTipItem.ID)
        {
          toolTip.Title = toolTipItem.Title;
          toolTip.Description = toolTipItem.Description;
          toolTip.AlternativeDescription = toolTipItem.AlternativeDescription;
          toolTip.ShowHelp = toolTipItem.ShowHelp;
          toolTip.ScreenTipSource = toolTipItem.ScreenTipSource;

          foundInCollection = true;
          break;
        }
      }

      if (!foundInCollection)
      {
        throw new Exception(string.Format("ToolTipID '{0}' not found in the ToolTipCollection", toolTipID));
      }

      double difference = ((FrameworkElement)Application.Current.RootVisual).ActualWidth - toolTipLocation.X - toolTip.ActualWidth;
      toolTip.Show(toolTipLocation.X + (difference < 0 ? difference : 0), toolTipLocation.Y, isAlternative);
    }
    #endregion

    #region HideToolTip
    internal void HideToolTip(string toolTipID)
    {
      toolTip.Hide();
    }
    #endregion
    #endregion

    #region Properties...
    #region Tabs
    public RibbonTabCollection Tabs
    {
      get { return (RibbonTabCollection)GetValue(TabsProperty); }
      set { SetValue(TabsProperty, value); }
    }
    #endregion

    #region Menus
    public MenuCollection Menus
    {
      get { return (MenuCollection)GetValue(MenusProperty); }
      set { SetValue(MenusProperty, value); }
    }
    #endregion

    #region QuickLaunchButtons
    public QuickLaunchButtonCollection QuickLaunchButtons
    {
      get { return (QuickLaunchButtonCollection)GetValue(QuickLaunchButtonsProperty); }
      set { SetValue(QuickLaunchButtonsProperty, value); }
    }
    #endregion

    #region ToolTips
    public ToolTipCollection ToolTips
    {
      get { return (ToolTipCollection)GetValue(ToolTipsProperty); }
      set { SetValue(ToolTipsProperty, value); }
    }
    #endregion

    #region OfficeButtonImageSource
    public string OfficeButtonImageSource
    {
      get { return (string)GetValue(OfficeButtonImageSourceProperty); }
      set { SetValue(OfficeButtonImageSourceProperty, value); }
    }
    #endregion

    #region TabContentDictionary
    internal Dictionary<RibbonTab, Grid> TabContentDictionary
    {
      get { return tabContentDictionary; }
    }
    #endregion

    #region SelectedTabIndex
    public int SelectedTabIndex
    {
      get { return (int)GetValue(SelectedTabIndexProperty); }
      set { SetValue(SelectedTabIndexProperty, value); }
    }
    #endregion

    #region HasMenuOpen
    public bool HasMenuOpen
    {
      get { return openMenu != null; }
    }
    #endregion
    #endregion
  }
  #endregion

  #region : RibbonTabCollection :
  public class RibbonTabCollection : ObservableCollection<RibbonTab>
  {
    #region Properties...
    #region AllLoaded
    public bool AllLoaded
    {
      get
      {
        bool allLoaded = true;

        foreach (RibbonTab tab in Items)
        {
          if (!tab.HasLoaded)
          {
            allLoaded = false;
            break;
          }
        }

        return allLoaded;
      }
    }
    #endregion
    #endregion
  }
  #endregion

  #region : ToolTipCollection :
  public class ToolTipCollection : ObservableCollection<ToolTip>
  {
  }
  #endregion

  #region : MenuCollection :
  public class MenuCollection : ObservableCollection<NsMenu.Menu>
  {
  }
  #endregion

  #region : QuickLaunchButtonCollection :
  public class QuickLaunchButtonCollection : ObservableCollection<RibbonButton>
  {
  }
  #endregion
}